﻿
//#define ONLINE_SFINAE_MVS

#ifndef ONLINE_SFINAE_MVS
    #pragma once
    #include "sfinae_detail.h"
#endif

//--------------------------------------------------------------

#define dSFINAE_CHECKER(member_name)                    \
    struct Checker: C                                   \
    {                                                   \
        template <class A1>                             \
            Checker(A1 a):C(a){}                        \
        template <class A1,class A2>                    \
            Checker(A1 a1,A2 a2):C(a1,a2){}             \
        template <class A1,class A2,class A3>           \
            Checker(A1 a1,A2 a2,A3 a3):C(a1,a2,a3){}    \
        using C::member_name;                           \
    };
//--------------------------------------------------------------

#define dSFINAE_CONST(member_name)     \
    ::tools::sfinae::Helper<typename ::tools::sfinae::Transform<U,T>::CMethod, &U::member_name>*
    
#define dSFINAE_NOCONST(member_name)   \
    ::tools::sfinae::Helper<typename ::tools::sfinae::Transform<U,T>::Method, &U::member_name>*

#define dSFINAE_VALIDATOR(member_name) \
    typename ::tools::sfinae::Select<C, T, decltype( std::declval<U>().member_name ) >::Result

//--------------------------------------------------------------

#define DETECTED_MEMBER(member_name)                                        \
template<class C,class T, int FUNC = ::std::is_function<T>::value >         \
struct has_##member_name                                                    \
{                                                                           \
    dSFINAE_CHECKER(member_name);                                           \
                                                                            \
    template<class U>static C CheckNoConst( dSFINAE_NOCONST(member_name) ); \
    template<class U>static void CheckNoConst( ... );                       \
    typedef decltype( CheckNoConst<Checker>(0) ) NoConstType;               \
                                                                            \
    template<class U>static C CheckConst( dSFINAE_CONST(member_name) );     \
    template<class U>static void CheckConst( ... );                         \
    typedef decltype( CheckConst<Checker>(0) ) ConstType;                   \
                                                                            \
    enum { eIS_NOCONST = std::is_same<NoConstType, C>::value };             \
    enum { eIS_CONST   = std::is_same<ConstType,   C>::value };             \
    enum { eIS_AVAILABLE = eIS_NOCONST||eIS_CONST };                        \
};                                                                          \
template<class C, class T> struct has_##member_name<C,T,0>                  \
{                                                                           \
    template<class U>static dSFINAE_VALIDATOR(member_name)                  \
        Check( decltype( std::declval<U>().member_name )* );                \
                                                                            \
    template<class U>static void Check(...);                                \
    typedef decltype( Check<C>(0) ) Type;                                   \
    enum { eIS_NOCONST   = 0 };                                             \
    enum { eIS_CONST     = 0 };                                             \
    enum { eIS_AVAILABLE = std::is_same<Type, C>::value };                  \
};


//------------------------------------------------------------------------------------
#define is_available(class_name, member_type, member_name)         \
    has_##member_name<class_name, member_type>::eIS_AVAILABLE

#define is_available_method(class_name, member_type, member_name)  \
    has_##member_name<class_name, member_type>::eIS_NOCONST

#define is_available_cmethod(class_name, member_type, member_name) \
    has_##member_name<class_name, member_type>::eIS_CONST
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------


#ifdef ONLINE_SFINAE_MVS

#include <type_traits>
#include <utility>

namespace tools{
    namespace sfinae{

        template<class U, U u> struct Helper{};
        //------------------------------------------------------

        template<class C, class T, class U> struct Select { typedef void  Result; };
        template<class C, class T> struct Select<C,T,T>   { typedef C     Result; };

        //------------------------------------------------------
        template<class C, class T> struct Transform;
        template<class C, class R> struct Transform<C,R()>
        {
            typedef R(C::*Method)();
            typedef R(C::*CMethod)()const;
        };
        template<class C, class R,class A1> struct Transform<C,R(A1)> 
        {
            typedef R(C::*Method)(A1);
            typedef R(C::*CMethod)(A1)const;
        };
        template<class C, class R,class A1,class A2> struct Transform<C,R(A1,A2)>  
        {
            typedef R(C::*Method)(A1,A2);
            typedef R(C::*CMethod)(A1,A2)const;
        };
        template<class C, class R,class A1,class A2,class A3> struct Transform<C,R(A1,A2,A3)>
        {
            typedef R(C::*Method)(A1,A2,A3);
            typedef R(C::*CMethod)(A1,A2,A3)const;
        };
        //------------------------------------------------------

    }//namespace sfinae
}//namespace tools


int g;
struct A
{
    A():r(g){}

    void fooA(int);
    void fooA(int)const;
    void fooA();
    void fooA()const;

    void foo();
    void foo()const;

    int v;
    int& r;
    int a[10];
    int* p;
    int** pp;
private:
    void private_foo();
};

struct B : A
{
    int v;
    int fooB(){ return 0; }
};

DETECTED_MEMBER(foo) ;
DETECTED_MEMBER(fooA);
DETECTED_MEMBER(fooB);
DETECTED_MEMBER(v);
DETECTED_MEMBER(r);
DETECTED_MEMBER(a);
DETECTED_MEMBER(p);
DETECTED_MEMBER(pp);
DETECTED_MEMBER(private_foo);

#include<iostream>
using namespace std;

int main()
{
    //синтаксис:  is_available(  имя_класса ,  тип_мембера  , имя_мембера)
    //вернет true если мембер доступен, иначе - false

    //синтаксис:  is_available_method(  имя_класса ,  тип_мембера  , имя_мембера)
    //вернет true если доступна не константная версия функции-члена

    //синтаксис:  is_available_cmethod(  имя_класса ,  тип_мембера  , имя_мембера)
    //вернет true если доступна константная версия функции-члена


    //---- детектирование данных
    cout<< "is available int B.v ? (most be 1) "
        << is_available(B,int,v)     <<endl;

    cout<< "is available int& B.r ?  (most be 1) "
        << is_available(B,int&,r)    <<endl;

    cout<< "is available int B.a[10] ?  (most be 1) "
        << is_available(B,int[10],a) <<endl;

    cout<< "is available int* B.p ?  (most be 1) "
        << is_available(B,int*,p)    <<endl;

    cout<< "is available int* B.pp ?  (most be 1) "
        << is_available(B,int**,pp)  <<endl;

    //---- детектирование методов
    cout<< "is available void B.fooA(int) or void B::fooA(int)const ?  (most be 1) "
        <<is_available(B,void(int),fooA) <<endl;

    cout<< "is available void B.fooA(int) ?  (most be 1) "
        << is_available_method(B,void(int),fooA) <<endl;

    cout<< "is available void B.fooA(int)const ?  (most be 1) "
        << is_available_cmethod(B,void(int),fooA) <<endl;

    cout<< "is available int B.fooB() ?  (most be 1) "
        << is_available_method(B,int(),fooB) <<endl;

//     cout<< "is available void B.private_foo() ?  (most be 0) "    
//         << is_available(B,void(),private_foo) <<endl;

    cout<< "is available void B.foo(int) ?  (most be 0) "    
        << is_available(B,void(int),foo) <<endl;
}


#endif
